package com.yonyou.common.service.messagecenter.rabbitmq;

import java.io.IOException;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.TimeoutException;

import org.apache.log4j.Logger;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.stereotype.Component;


import com.rabbitmq.client.Address;
import com.rabbitmq.client.Channel;
import com.rabbitmq.client.Connection;
import com.rabbitmq.client.ConnectionFactory;
import com.yonyou.common.service.messagecenter.config.RabbitMQConfig;

@Aspect
@Component
public class ConnectionPool {
	@Autowired
	@Qualifier("connectionFactory")
    private ConnectionFactory factory;
    @Autowired
    private RabbitMQConfig config;
	private static Logger logger=Logger.getLogger(ConnectionPool.class);
	private CopyOnWriteArrayList<ConnectionWrapper> frees=new CopyOnWriteArrayList();
	private CopyOnWriteArrayList<ConnectionWrapper> uses=new CopyOnWriteArrayList();

	public ConnectionWrapper getConnection() {
//		logger.info("--getConnection");
		ConnectionWrapper n=null;
		long t1=System.currentTimeMillis();
		while (n==null){
			long t2=System.currentTimeMillis();
			if(t2-t1>=config.getTimeout()*1000){
				logger.info("--get Connection timeout---");
				break;
			}
			n=get();
		}
		return n;
	}
	private synchronized ConnectionWrapper get() {
		ConnectionWrapper connection=null;
		if(frees.size()==0 && uses.size()>=config.getMaxConnection()){
			return null;
		}
		if(frees.size()==0){
			try {
				logger.info("--new Connection---");
				logger.info("--frees.size="+frees.size()+",uses.size="+uses.size());
				Connection n=newConnection();
				connection = new ConnectionWrapper(Thread.currentThread().getId(),n);
				uses.add(connection);
			} catch (Exception e) {
				logger.error(e.getMessage());
			} 
		}else{
			logger.info("--reuse Connection---");
			connection=frees.get(0);
			connection.setId(Thread.currentThread().getId());
			frees.remove(connection);
			uses.add(connection);
		}
		return connection;
	}
	
	private Connection newConnection() throws IOException, TimeoutException {
		Connection n=null;
		logger.info("--mq hosts:"+config.getHost());
		String[] hosts=config.getHost().split(",");
//		String[] ports=config.getPort().split(",");
		Address[] addrs=new Address[hosts.length];
		for(int i=0;i<hosts.length;i++){
			Address addr=new Address(hosts[i],Integer.parseInt(config.getPort()));
			addrs[i]=addr;
		}
		n = factory.newConnection(addrs);
		return n;
	}

	@Around("execution(* com.yonyou.common.service.messagecenter.rabbitmq.MessageProducer.*SendMessage(..))")
	public Object freeSendConnection(ProceedingJoinPoint joinPoint) throws Throwable {
		Object result =null;
		try{
			result = joinPoint.proceed();
		}finally{
			freeId(Thread.currentThread().getId());
			logger.info("--freeConnection finish id="+Thread.currentThread().getId());
		}
		return result;
	}

	private synchronized void freeId(long id){
//		logger.info("--begin free connection for id="+id);
		String sk="";
		boolean ok=false;
		Iterator<ConnectionWrapper> it = uses.iterator();  
        while(it.hasNext()) {  
        	ConnectionWrapper s = it.next();  
//			logger.info("--used id ="+s.getId());
        	sk=sk+s.getId();
        	if(s.getId()==id){
				uses.remove(s);
				frees.add(s);
//				logger.info("--freeId="+id+"--frees.size="+frees.size()+",uses.size="+uses.size());
				ok=true;
				break;
			}
        } 
        if(!ok)
        	logger.info("not find id "+id+" in "+sk);
	}
}
class ConnectionWrapper{
	private static Logger logger=Logger.getLogger(ConnectionPool.class);
	private long id;
	private Connection con;
	private Channel channel;
	public ConnectionWrapper(long id, Connection con){
		this.id=id;
		this.con=con;
	}
	public long getId() {
		return id;
	}
	public void setId(long id) {
		this.id = id;
	}
	public Connection getCon() {
		return con;
	}
	public void setCon(Connection con) {
		this.con = con;
	}
	public Channel getChannel()  {
		if(channel==null){
			try {
				channel=this.con.createChannel();
			} catch (IOException e) {
				logger.info(e.getMessage());
			}
		}
		return channel;
	}
	public void setChannel(Channel channel) {
		this.channel = channel;
	}
	
}
